// Copyright©,2020-2025
// Author: caocheng
// Version: 1.0.0
// Date: 2021/3/10 14:25
// Description：
package SummerScanner

import (
	"gitee.com/chrischo/go-summer/summer/summer-logger"
	"gitee.com/chrischo/go-summer/summer/summer-web"
	"github.com/go-openapi/spec"
	"github.com/spf13/cast"
	"strconv"
	"strings"
	"unicode"
)

const (
	TagParam       = "@Param"
	TagRouter      = "@Router"
	TagDescription = "@Description"
	TagSuccess     = "@Success"
	TagFailure     = "@Failure"
)

type Router struct {
	Path    string
	Methods []string
}

type Comment struct {
	Params      []*spec.Parameter
	Router      Router
	Description string
	Resp        map[int]*spec.Response
}

type CommentParser struct {
}

func NewCommentParser() *CommentParser {
	return &CommentParser{}
}

func (p *CommentParser) ParserComments(comments []string) Comment {
	var comment Comment
	for _, v := range comments {
		str := strings.TrimSpace(strings.TrimLeft(v, "//"))
		arr := p.parserString(str)
		if len(arr) == 0 {
			continue
		}
		switch arr[0] {
		case TagParam:
			comment.Params = append(comment.Params, p.parserParam(arr))
		case TagDescription:
			comment.Description = p.parserDescription(arr)
		case TagRouter:
			comment.Router = p.parserRouter(arr)
		case TagSuccess:
		case TagFailure:
			code, resp := p.parserResponse(arr)
			if comment.Resp == nil {
				comment.Resp = make(map[int]*spec.Response)
			}
			comment.Resp[code] = resp
		}
	}

	return comment
}

func (p *CommentParser) parserString(str string) []string {
	var s []rune
	var j int
	var start bool
	var r []string
	var quoted int8
	for _, c := range str {
		if unicode.IsSpace(c) && quoted == 0 {
			if !start {
				continue
			} else {
				start = false
				j++
				r = append(r, string(s))
				s = make([]rune, 0)
				continue
			}
		}

		start = true
		if c == '"' || c == '“' {
			quoted ^= 1
			continue
		}
		s = append(s, c)
	}
	if len(s) > 0 {
		r = append(r, string(s))
	}
	return r
}

func (p *CommentParser) parserParam(arr []string) *spec.Parameter {
	if len(arr) < 4 {
		SummerLogger.Error("Invalid @Param format. Needs at least 4 parameters")
		return nil
	}
	var param *spec.Parameter
	switch arr[2] {
	case "header":
		param = SummerWeb.HeaderParam(arr[1], arr[3], "")
	case "path":
		param = SummerWeb.PathParam(arr[1], arr[3], "")
	case "body":
		schema := spec.RefSchema("#/definitions/" + arr[3])
		param = SummerWeb.BodyParam(arr[1], schema)
	}

	if param != nil {
		switch len(arr) {
		case 6:
			param.Required, _ = strconv.ParseBool(arr[4])
			param.Description = arr[5]
		case 7:
			param.Default = arr[4]
			param.Required, _ = strconv.ParseBool(arr[5])
			param.Description = arr[6]
		}
	}
	return param
}

func (p *CommentParser) parserDescription(arr []string) string {
	if len(arr) < 2 {
		return ""
	}
	return arr[1]
}

func (p *CommentParser) parserRouter(arr []string) Router {
	if len(arr) != 3 {
		return Router{}
	}
	var router Router
	router.Path = arr[1]
	methods := strings.ToUpper(strings.Trim(arr[2], "[]"))
	if methods == "" {
		router.Methods = []string{"GET"}
	} else {
		router.Methods = strings.Split(methods, ",")
	}
	return router
}

func (p *CommentParser) parserResponse(arr []string) (int, *spec.Response) {
	if len(arr) < 4 {
		SummerLogger.Error("Invalid Response format. Needs at least 4 parameters")
		return 0, nil
	}
	resp := new(spec.Response)
	if arr[2] == "{object}" || arr[2] == "{array}" {
		schema := spec.RefSchema("#/definitions/" + arr[3])
		if arr[2] == "{array}" {
			resp.WithSchema(spec.ArrayProperty(schema))
		} else {
			resp.WithSchema(schema)
		}
	} else {
		resp.WithSchema(&spec.Schema{SchemaProps: spec.SchemaProps{Type: []string{arr[3]}}})
	}

	if len(arr) > 4 {
		resp.WithDescription(arr[4])
	}

	return cast.ToInt(arr[1]), resp
}
